package com.gambi.tapestry.services.gwt.internal;

import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;

import org.apache.tapestry5.runtime.Component;
import org.apache.tapestry5.services.ComponentClassResolver;
import org.apache.tapestry5.services.ComponentSource;
import org.apache.tapestry5.services.Request;
import org.apache.tapestry5.services.Response;
import org.slf4j.Logger;

import com.gambi.tapestry.services.gwt.GWTRemoteServiceHandler;
import com.gambi.tapestry.utils.GWTConstant;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.server.rpc.RPC;
import com.google.gwt.user.server.rpc.RPCRequest;

public class GWTRemoteServicetHandlerImpl implements GWTRemoteServiceHandler {

	private final ComponentSource componentSource;
	private final ComponentClassResolver componentClassResolver;
	
	public GWTRemoteServicetHandlerImpl(Logger logger,
			ComponentSource componentSource,
			ComponentClassResolver componentClassResolver) {
		this.componentSource = componentSource;
		this.componentClassResolver = componentClassResolver;
	}

	/*
	 * TODO Can be moved to some UTILITY CLASS ?
	 */
	boolean sendResponse(String payload, Response response) throws IOException {
		if (payload == null)
			return false;
		PrintWriter pw = response
				.getPrintWriter("application/json; charset=utf-8");
		pw.print(payload);
		pw.flush();
		return true;
	}

	/*
	 * TODO Can be moved to some UTILITY CLASS ?
	 */
	String encodeRPCSuccess(Method method, Object object) throws IOException {
		try {
			return RPC.encodeResponseForSuccess(method, object);
		} catch (SerializationException e) {
			throw new IOException(e.getMessage());
		}
	}

	/*
	 * TODO Can be moved to some UTILITY CLASS ?
	 */
	String encodeRPCFailure(Method method, Exception cause) throws IOException {
		try {
			return RPC.encodeResponseForFailure(method, cause);
		} catch (SerializationException e) {
			throw new IOException(e.getMessage());
		}
	}

	Method findHandlerMethod(Object component, RPCRequest rpcRequest)
			throws IOException {
		try {
			Method method = rpcRequest.getMethod();
			Class<?>[] parameterTypes = method.getParameterTypes();

			return component.getClass().getMethod(method.getName(),
					parameterTypes);
		} catch (SecurityException e) {
			throw new IOException(e.getMessage());
		} catch (NoSuchMethodException e) {
			return null;
		}
	}

	@Override
	public boolean handleRequest(Request request, Response response)
			throws IOException {

		// If we can assume that the requests are all identified by the URL
		if( ! request.getPath().startsWith( GWTConstant.GWT_REMOTE_SERVICE_PREFIX ) ){
			return false;
		}

		try {
			
			// Recover the page
			String pageName = request.getPath().replaceAll(
					GWTConstant.GWT_REMOTE_SERVICE_PREFIX, "");
			// /gwtservice/checkboxremoteservice
			if( pageName.startsWith("/") ){
				pageName = pageName.substring(1, pageName.length());
			}
			// gwtservice/checkboxremoteservice

			if( ! componentClassResolver.isPageName( pageName ) ){
				System.out
						.println("GWTRemoteServicetHandlerImpl.handleRequest() THIS is not a page... let Tapestry manage it.");
			} 
			Component servicePage = componentSource
					.getPage( pageName );

			/*
			 * The GWT Request targets a Tapestry Page-Service
			 */
			String payload = handleServiceRequest(servicePage,
					(RPCRequest) request.getAttribute(RPCRequest.class
							.getName()));
			return sendResponse(payload, response);

		} catch (Exception e) {
			e.printStackTrace();
			System.out
					.println("GWTRemoteServicetHandlerImpl.handleRequest(). NOT A VALID SERVICE PAGE. Pass the request forward");
			return false;
		}

	}

	/*
	 * TODO Here a better approach would be to look for the right service...
	 * something like the eventLinkEncoded... but for service.
	 * Where the URL contains the ID of the service (or just the GWT Service)
	 * that can be contributed in the AppModule as a library
	 */
	private String handleServiceRequest(Component servicePage,
			RPCRequest rpcRequest) {

		try {
			return RPC.invokeAndEncodeResponse(servicePage,
					rpcRequest.getMethod(), rpcRequest.getParameters());
		} catch (SerializationException e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}

	String componentID(Request request) {
		// Remove any ref to the event if any
		String path = request.getPath();

		if (path.contains(":")) {
			path = path.split(":")[0];
		}

		path = path.replaceFirst("/", "");
		String id = path.replace(".", ":");

		System.out.println("GWTRemoteServicetHandlerImpl.componentID() " + id);
		return id;
	}

}
